/* Copyright  2009
   @Author
   Richard Changde Yin            e-mail yinchangde@hotmail.com

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/**
 * @filename:mutex.cc
 */

#include <server_includes.h>
#include <os.h>
#include <mutex.h>
#include <debug.h>
#include <nanoTimer.h>
#include <config.h>
#include <process.h>

#include <read-config.h>

#include <gettext.h>
#include <error.h>
#include <errmsg_print.h>

Mutex::Mutex()
{
#if defined(sparc) || defined(i686) ||defined(x86_64)
    lock =0;
#else
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
    pthread_mutex_init(&mutex_, &attr);
#endif
}
Mutex::~Mutex()
{
}
int Mutex::init()
{
#if defined(sparc) || defined(i686)|| defined(x86_64)

    lock = 0;
#else
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
    pthread_mutex_init(&mutex_, &attr);
    pthread_mutexattr_destroy(&attr);
#endif
    return 0;
}

int Mutex::init(char *mname)
{
    if (strlen(mname) > 19 || !mname) return 1;
    init();
    strcpy(name, mname);
    return 0;
}

#if defined(sparc) || defined(i686)|| defined(x86_64)

int TSL(Lock *lock)
{
/*
    if (lock == 0) {
       __asm("mov  $1, %eax");
       __asm("mov 20(%ebp), %edx");
       __asm("xchg %eax, (%edx)");
       __asm("test %eax %eax");
       __asm("jnz .L1000");
       return 0;
    } else
    {
        __asm(".L1000:");
        return 1;
    }
*/
/*Was Working in linux
        char oldval;
        __asm__ __volatile__(
                "xchgb %b0,%1"
                :"=q" (oldval), "=m" (lock)
                :"0" (0) : "memory");

        return oldval > 0;
*/
#if defined(i686) || defined(x86_64)

    int*  lw;
    int   res;
    lw = (int*)lock;
    if (*lock == 1) return 1;
    /* In assembly we use the so-called AT & T syntax where
    the order of operands is inverted compared to the ordinary Intel
    syntax. The 'l' after the mnemonics denotes a 32-bit operation.
    The line after the code tells which values come out of the asm
    code, and the second line tells the input to the asm code. */

    /* This assembly compiles only with -O2 option, and not with -g option. Version1
    __asm__ __volatile__(
        "movl $1, %%eax; xchgl (%%ecx), %%eax"
        : "=eax" (res), "=m" (*lw)
        : "ecx" (lw));
    */

     /* This assembly takes lot of time for test/performance/DMLTest. Version2
     __asm__ __volatile__(
	"movl %1, %0; xchgl %0, %2"
	: "=r" (res), "=r" (lock)
        : "r" (lock));
     */


    // This assembly is Version3. Working fine for now
    __asm__ __volatile__(
        "xchgl %0, %1 \n\t"
        : "=r"(res), "=m"(*lock)
        : "0"(1), "m"(*lock)
        : "memory");


    return(res);

#elif defined (sparc)
    Lock res;
    __asm__ __volatile__("ldstub  [%2], %0    \n"
       "=r"(res), "+m"(*lock)
       "r"(lock)
       "memory");
     return (int) res;
#endif
}
#endif

int Mutex::tryLock(int tryTimes, int waitmsecs)
{
    int tries = 0;
    int ret = 0;
    struct timeval timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = waitmsecs;
    if (tryTimes == 0 && waitmsecs == 0)
    {
        timeout.tv_sec =  Conf::config.getMutexSecs();
        timeout.tv_usec = Conf::config.getMutexUSecs();
        tryTimes       =  Conf::config.getMutexRetries();
    }
    while (tries < tryTimes)
    {
#if defined(sparc) || defined(i686) || defined(x86_64)

    if (TSL(&lock) == 0)
    {
        return ret;
    }
#else
    ret = pthread_mutex_trylock(&mutex_);
    if (EBUSY  != ret) return 0;

#endif
        ::select(0, 0, 0, 0, &timeout);
        tries++;
    }
    return 1;
}


int Mutex::getLock(int procSlot, bool procAccount)
{
    int ret=0;
#if defined(sparc) || defined(i686) || defined(x86_64)

    ret = tryLock();
    //add it to the has_ of the ThreadInfo
    if (ret ==0 && procAccount) ProcessManager::addMutex(this, procSlot);

    return ret;
#else
    ret = pthread_mutex_lock(&mutex_);

#endif
    if (!ret) return ret ;
    else
    {
        switch(ret)
      {
        case EBUSY:
          return HA_ERR_LOCK_WAIT_TIMEOUT;
        case EDEADLK:
          return HA_ERR_LOCK_DEADLOCK;
        default:
         return HA_ERR_SYS_INTERNAL ;
      }
    }

}

int Mutex::releaseLock(int procSlot, bool procAccount)
{
    int ret=0;
#if defined(sparc) || defined(i686) || defined(x86_64)

    /*int *lw = &lock;
    if (*lw == 0) return 0;
    __asm__ __volatile__("movl $0, %%eax; xchgl (%%ecx), %%eax" :
                      "=m" (*lw) :
                      "ecx" (lw) :
                      "eax");
    */
    lock = 0;
#else
    ret = pthread_mutex_unlock(&mutex_);
#endif
    if (ret == 0 && procAccount)
    {
        ProcessManager::removeMutex(this, procSlot);
        return ret;
    }
    else
        return 1;
}

int Mutex::destroy()
{
#if defined(sparc) || defined(i686)|| defined(x86_64)

#else
    return pthread_mutex_destroy(&mutex_);
#endif
    return 0;
}


int Mutex::recoverMutex()
{
    int ret=0;
#if defined(sparc) || defined(i686) || defined(x86_64)
  /*int *lw = &lock;
   if (*lw == 0) return 0;
   __asm__ __volatile__("movl $0, %%eax; xchgl (%%ecx), %%eax" :
                       "=m" (*lw) :
                      "ecx" (lw) :
                      "eax");
    */
   lock = 0;
 #else
  ret = pthread_mutex_unlock(&mutex_);
#endif
   return ret;
}

